﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.MultiTenancy;

namespace Loong.Admin.Permissions
{
    public class PermissionAppService : AdminAppService
    {
        private readonly IPermissionDefinitionManager _permissionDefinitionManager;
        private readonly IPermissionGrantRepository _permissionGrantRepository;

        public PermissionAppService(
            IPermissionDefinitionManager permissionDefinitionManager,
            IPermissionGrantRepository permissionGrantRepository)
        {
            _permissionDefinitionManager = permissionDefinitionManager;
            _permissionGrantRepository = permissionGrantRepository;
        }

        public async Task<List<PermissionGroupDto>> GetAsync(string role)
        {
            var providerName = RolePermissionValueProvider.ProviderName;
            var result = new List<PermissionGroupDto>();

            var multiTenancySide = CurrentTenant.GetMultiTenancySide();

            foreach (var group in await _permissionDefinitionManager.GetGroupsAsync())
            {
                var groupDto = BuildPermissionGroupDto(group);

                var neededCheckPermissions = new List<PermissionDefinition>();

                var permissions = group.GetPermissionsWithChildren()
                    .Where(x => x.IsEnabled)
                    .Where(x => !x.Providers.Any() || x.Providers.Contains(providerName))
                    .Where(x => x.MultiTenancySide.HasFlag(multiTenancySide));

                foreach (var permission in permissions)
                {
                    if (permission.Parent != null && !neededCheckPermissions.Contains(permission.Parent))
                    {
                        continue;
                    }

                    neededCheckPermissions.Add(permission);
                }

                if (!neededCheckPermissions.Any())
                {
                    continue;
                }

                var permissionGrants = await _permissionGrantRepository.GetListAsync(neededCheckPermissions.Select(x => x.Name).ToArray(), providerName, role);

                var rootPermissions = neededCheckPermissions
                    .Where(x => x.Parent == null)
                    .ToList();
                foreach (var permission in rootPermissions)
                {
                    groupDto.Permissions.Add(BuildPermissionTree(permission, p => permissionGrants.Any(i => i.Name == p.Name)));
                }

                if (groupDto.Permissions.Any())
                {
                    result.Add(groupDto);
                }
            }

            return result;
        }

        private PermissionGrantInfoDto BuildPermissionTree(PermissionDefinition permission, Func<PermissionDefinition, bool> predicate)
        {
            var grantInfo = new PermissionGrantInfoDto
            {
                Name = permission.Name,
                DisplayName = permission.DisplayName.Localize(StringLocalizerFactory),
                IsGranted = predicate(permission)
            };

            foreach (var child in permission.Children)
            {
                grantInfo.Permissions.Add(BuildPermissionTree(child, predicate));
            }

            return grantInfo;
        }

        private PermissionGroupDto BuildPermissionGroupDto(PermissionGroupDefinition group)
        {
            return new PermissionGroupDto
            {
                Name = group.Name,
                DisplayName = group.DisplayName.Localize(StringLocalizerFactory)
            };
        }
    }
}
